#include <iostream>
#include <fstream>
#include <stack>
#include "image.h"
#include "ReadImageHeader.cpp"
#include "ReadImage.cpp"
#include "WriteImage.cpp"
#include "heap.h"

 

using namespace std;



void findBin( tree T, int array[][100] );

void biniter( weightedpixel * pixel, int arr[], int array[][100], int y, const bool & left );

void CompressImage( ImageType image, int arr[][100], const char filename[] );

void UncrompressImage( ImageType image, tree * huffman, const char filename[] );
      

int main()
{
    int row, col, max, selection, pixel;
    int x_in, x_col, y_in, y_col;
    bool test = false;
    char file_name[20], out_name[20], image_1[20], image_2[20], sel;
    int** maker;
    char cont;
    ImageType tester;
    
    cout << "------- Program Tester -------" << endl;
    cout << "Input File Name: ";
    cin >> file_name;
    
    //Read Information from file to dummy ints
    cout << endl << "Retrieving Header Information" << endl;
    readImageHeader( file_name, row, col, max, test );
    cout << "Completed." << endl;
    
    // Define image
    ImageType picture( row, col, max);
	ImageType picture_2( 1024, 256, max);
    
    // Read image from file to array
    cout << endl << "Retrieving File Informtation..." << endl;
    readImage( file_name, picture );
    cout << "Completed." << endl;

	

    // Display Menu
    do{
        cout << " ===== MAIN MENU =====" << endl;
        cout << "1. getImageInfo" << endl;
        cout << "2. getPixelVal" << endl;
        cout << "3. setPixelVal" << endl;
        cout << "4. getSubImage" << endl;
        cout << "5. meanGray" << endl;
        cout << "6. enlargeImage" << endl;
        cout << "7. shrinkImage" << endl;
        cout << "8. reflectImage" << endl;
        cout << "9. translateImage" << endl;
        cout << "10. rotateImage" << endl;
        cout << "11. Add Images" << endl;
        cout << "12. Subtract Images" << endl;
        cout << "13. Negate Image" << endl;
        cout << "14. Compress Image" << endl;
        cout << "15. Quit" << endl;
        cin >> selection;
        cout << endl << endl;
        
        switch( selection )
        {
            case 1:
            cout << "-- getImageInfo -- " << endl;
            picture.getImageInfo( row, col, max );
            cout << "Rows: " << row << endl;
            cout << "Columns: " << col << endl;
            cout << "Maximum Grayscale Value: " << max << endl;
            cout << endl << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 2:
            cout << "-- getPixelVal --" << endl;
            cout << "Enter X Position: ";
            cin >> x_in;
            cout << "Enter Y Position: ";
            cin >> y_in;
            picture.getPixelVal( x_in, y_in, pixel );
            if( pixel >= 0 )
            {
                cout << "Value: " << pixel << endl << endl;
            }
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 3:
            cout << "-- setPixelVal --" << endl;
            cout << "Enter New Pixel Value: ";
            cin >> pixel;
            cout << "Enter X Position: ";
            cin >> x_in;
            cout << "Enter Y Position: ";
            cin >> y_in;
            test = picture.setPixelVal( x_in, y_in, pixel );
            if( test == true )
            {
            cout << endl << "New value set." << endl;
            picture.getPixelVal( x_in, y_in, pixel );            
            cout << "Value: " << pixel << endl << endl;
            }
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 4:
            cout << "-- getSubImage --" << endl;
            
            cout << "Enter the Upper Left Row: " << endl;
            cin >> x_in;
            cout << "Enter the Upper Left Column: " << endl;
            cin >> x_col;
            cout << "Enter the Lower Right Row: " << endl;
            cin >> y_in;
            cout << "Enter the Lower Right Column: " << endl;
            cin >> y_col;
            
            cout << endl << endl;
            
            test = tester.getSubImage( x_in, x_col, y_in, y_col, picture );
            
            cout << "Enter desired file name: ";
            cin >> out_name;
            
            if( test == true )
            {
                writeImage( out_name, tester );
            }
            else
            cout << "Error: There was a problem writing the image to file" << endl;
            cout << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 5:
            cout << "-- meanGray --" << endl;
            pixel = picture.meanGray();
            cout << "The mean value of the image is: " << pixel << endl << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 6:
            cout << "-- enlargeImage --" << endl;
            cout << "Enter the scale to enlarge: " << endl;
            cin >> pixel;
            
            test = tester.enlargeImage( pixel, picture );
            
            cout << "Enter desired file name: ";
            cin >> out_name;
            
            if( test == true )
            {
                writeImage( out_name, tester );
            }
            else
            cout << "Error: There was a problem writing the image to file" << endl;
            cout << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 7:
            cout << "-- shrinkImage --" << endl;
            cout << "Enter the factor by which it should be shrunk: ";
            cin >> pixel;
            
            test = tester.shrinkImage( pixel, picture );
            
            cout << "Enter desired file name: ";
            cin >> out_name;
            
            if( test == true )
            {
                writeImage( out_name, tester );
            }
            else
            cout << "Error: There was a problem writing the image to file" << endl;
            cout << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 8:
            cout << "-- reflectImage -- " << endl;
            while( (sel != 'h') && (sel != 'v' ) )
            {
                cout << "Flip horizontal or vertical? <h/v>: ";
                cin >> sel;
            }
            if( sel == 'h' )
            {
                test = true;
            }
            else
            {
                test = false;
            }
            
            test = tester.reflectImage( test, picture );
            
            cout << "Enter desired file name: ";
            cin >> out_name;
            
            if( test == true )
            {
                writeImage( out_name, tester );
            }
            else
            cout << "Error: There was a problem writing the image to file" << endl;
            cout << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            sel = 'a';
            break;
            
            
            case 9:
            cout << "-- translateImage --" << endl;
            cout << "Enter amount to translate: ";
            cin >> pixel;
            
            test = tester.translateImage( pixel, picture );
            
            cout << "Enter desired file name: ";
            cin >> out_name;
            
            if( test == true )
            {
                writeImage( out_name, tester );
            }
            else
            cout << "Error: There was a problem writing the image to file" << endl;
            cout << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 10:
            cout << "-- rotateImage --" << endl;
            cout << "Enter angle of rotation: ";
            cin >> pixel;
            
            test = tester.rotateImage( pixel, picture );
            
            cout << "Enter desired file name: ";
            cin >> out_name;
            
            if( test == true )
            {
                writeImage( out_name, tester );
            }
            else
            cout << "Error: There was a problem writing the image to file" << endl;
            cout << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 11:
            {
            cout << "-- overloaded + operator --" << endl;
            cout << "Enter file 1: ";
            cin >> image_1;
            //Read Information from file to dummy ints
            cout << endl << "Retrieving Header Information" << endl;
            readImageHeader( image_1, row, col, max, test );
            cout << "Completed." << endl;
            
            // Define image
            ImageType img_1( row, col, max);
            
            // Read image from file to array
            cout << endl << "Retrieving File Informtation..." << endl;
            readImage( image_1, img_1 );
            cout << "Completed." << endl << endl << endl;
            
            cout << "Enter file 2: ";
            cin >> image_2;
            //Read Information from file to dummy ints
            cout << endl << "Retrieving Header Information" << endl;
            readImageHeader( image_2, row, col, max, test );
            cout << "Completed." << endl;
            
            // Define image
            ImageType img_2( row, col, max);
            
            // Read image from file to array
            cout << endl << "Retrieving File Informtation..." << endl;
            readImage( image_2, img_2 );
            cout << "Completed." << endl << endl << endl;
            
            img_1 + img_2;

            cout << "Enter desired file name: ";
            cin >> out_name;

            writeImage( out_name, img_1 );
                
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            }
            
            case 12:
            {
            cout << "-- overloaded - operator --" << endl;
            cout << "Enter file 1: ";
            cin >> image_1;
            //Read Information from file to dummy ints
            cout << endl << "Retrieving Header Information" << endl;
            readImageHeader( image_1, row, col, max, test );
            cout << "Completed." << endl;
            
            // Define image
            ImageType sub_1( row, col, max);
            
            // Read image from file to array
            cout << endl << "Retrieving File Informtation..." << endl;
            readImage( image_1, sub_1 );
            cout << "Completed." << endl << endl << endl;
            
            cout << "Enter file 2: ";
            cin >> image_2;
            //Read Information from file to dummy ints
            cout << endl << "Retrieving Header Information" << endl;
            readImageHeader( image_2, row, col, max, test );
            cout << "Completed." << endl;
            
            // Define image
            ImageType sub_2( row, col, max);
            
            // Read image from file to array
            cout << endl << "Retrieving File Informtation..." << endl;
            readImage( image_2, sub_2 );
            cout << "Completed." << endl << endl << endl;
            
            sub_1 - sub_2;

            cout << "Enter desired file name: ";
            cin >> out_name;

            writeImage( out_name, sub_1 );
                
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            
            break;
            }
            
            case 13:
            cout << "-- negateImage --" << endl;
                        
            test = tester.negateImage( picture );
            
            cout << "Enter desired file name: ";
            cin >> out_name;
            
            if( test == true )
            {
                writeImage( out_name, tester );
            }
            else
            cout << "Error: There was a problem writing the image to file" << endl;
            cout << endl;
            
            while( cont != 'y' )
            {
                cout << "Continue? <y/n>" << endl;
                cin >> cont;
            }
            cont = 'n';
            break;
            
            case 14:
			{
				int hist[256], array[256][100];
				tree temp_1, temp_2;
				weightedpixel *print, *tmp;
				PQType Que( 256 );
				queue preQ(256);  
				queue printQue(256);
				
				for( int i = 0; i < 256; i++ )
				{
					for( int j = 0; j < 100; j++ )
					{
						array[i][j] = -1;
					}
				}
				
				cout << "--Compress Image--" << endl;
            
				// GENERATE HISTOGRAM 
					test = picture_2.genhist( picture, hist );

            
				cout << "Enter desired file name for Histogram: ";
		 		cin >> out_name;
            
				if( test == true )
				{
					writeImage( out_name, picture_2 );
				}
				else
				cout << "Error: There was a problem writing the image to file" << endl;
				cout << endl;
				
				cout << "Creating Priority Queue . . . " << endl << endl;
				// loop through histogram and push into queueueueueueueue
				tree *tempTree;  
				char junk;
				for( int i =0; i < 256; i++ )
				{
					// 
					  
					tempTree = new tree;

					// set from hist. to tree info
					 
					(*tempTree->tip).set( hist[i], i, true );
					tempTree->tip->left = NULL;
					tempTree->tip->right = NULL;
					 
					// Stick the tree in the queueueueueueueueueue
					Que.Enqueue( *tempTree );
				}
								
				cin >> junk;
				cout << "Creating Tree . . . " << endl << endl;
				//Create the tree
				while( Que.numItems > 1 )
				{
					
					Que.Dequeue( temp_1 );
					Que.Dequeue( temp_2 );
					temp_1.merge( temp_2 );
					Que.Enqueue( temp_1 );
					 
				}
				Que.Dequeue( temp_1 );
	
				cout << "Building Decoding Table . . . " << endl << endl;
				// Build Decoding table
				findBin( temp_1, array );
				
				cout << "Printing Decoding Table . . . " << endl << endl;
				// Print Decoding Table
				cout << "Decoding Table" << endl;
				cout << "------------------------------------------" << endl;
				cout << "Pixel Value       |Binary Representation  " << endl;
				
				/*for( int i = 0; i < 256; i++ )
				{
					cout << i << ":              | ";
					for( int j = 0; j < 10; j++ )
					{ 
						cout << array[i][j] << ",";
					}
					cout << endl;
				}*/
				
				while( cont != 'y' )
				{
					cout << "Continue? <y/n>" << endl;
					cin >> cont;
				}
				cont = 'n';
				break;
			
				
			}
			
			
        }
        
    }while( selection != 15 );
    
    return 0;
}


void findBin( tree T, int array[][100] )
{
	weightedpixel* temp;
	temp = T.tip;
	int y = 0;
	int arr[256];
	biniter( temp, arr, array, y, true);
}

void biniter( weightedpixel *pixel, int arr[], int array[][100], int y, const bool & left )
{
	stack <weightedpixel*>stacker;
	weightedpixel* temp;
	
	stacker.push(pixel);
	while( !stacker.empty() )
	{
		temp = stacker.top();
		stacker.pop();
		
		cout << temp->weight <<  " " << temp->pixelval << " " << temp->isLeaf <<endl;
		
		if( temp->left != NULL )
			stacker.push(temp->left);
			
		if( temp->right != NULL)
			stacker.push( temp->right );
			
		
			
	}
	
	
	
	
	/*if( pixel != NULL )
	{
		cout << "count" << endl;
		biniter( pixel->left, arr, array, y+1, true );
		biniter( pixel->right, arr, array, y+1, false );
		
		if( (*pixel).isLeaf )
		{
			if( left )
			{
				arr[y] = 0;
				cout << "LEFT" << endl;
			}
			else
			{
				arr[y] = 1;
				cout << "RIGHT" << endl;
			}

			for( int i = 0; i <= y; i++ )
			{
				cout << pixel->pixelval << endl;
				array[pixel->pixelval][i] = arr[i];
			}
			array[pixel->pixelval][y+1] = -1;
		}
		else
		{
			if( left )
			{
				arr[y] = 0;
				cout << "OTHER LEFT" << endl;
				cout << pixel->weight << endl;
			}
			else
			{
				arr[y] = 1;
				cout << "OTHER RIGHT" << endl;
			}
		
			biniter( pixel->left, arr, array, y+1, true );
			biniter( pixel->right, arr, array, y+1, false );
		}
	}*/
}

void CompressImage( ImageType image, int arr[][100], const char filename[] )
{
	unsigned char compress = 0;
	int rows, cols, levels;
	int index = 0;
	int counter = 0;
	ofstream fout;
	fout.clear();
	fout.open( filename );
	

	image.getImageInfo( rows, cols, levels );
	// iterate through image
	for( int i = 0; i < rows; i++ )
	{
		for( int j = 0; j < cols; j++ )
		{
			image.getPixelVal( i, j, levels );
			
			while( arr[levels][counter] != -1 && counter < 100 )
			{
				compress <<= 1;
				index++;
				compress = compress | (arr[levels][counter] == 1);
				counter++;
				if( index == 8 )
				{
					fout << compress;
					compress = 0;
					index = 0;
				}
			}
			counter = 0;
			
		}
	}

	compress <<= (8-index);
	fout << compress;

	fout.close();
}

void UncrompressImage( ImageType image, tree * huffman, const char filename[] )
{
	ifstream fin;
	weightedpixel * huff;

	int rows, cols, levels;
	unsigned char uncompress, temp;
	bool leaf = false;
	int index= 0;

	image.getImageInfo( rows, cols, levels );

	fin.clear();
	fin.open( filename );

	fin >> uncompress;

	for( int i = 0; i < rows; i++ )
	{
		for( int j = 0; j < cols; j++ )
		{

			huff = (*huffman).tip;

			while( !leaf )
			{
				temp = uncompress;
				temp >>= (8-index);
				temp &= 254;
				index++;
				if( index > 8 )
				{
					index = 0;
					fin >> uncompress;
				}
				
				if( huff != NULL )
				{
					if( temp == true )
					{
						huff = (*huff).left;
					}
					else
					{
						huff = (*huff).left;
					}
				}

				if( (*huff).isLeaf )
				{
					leaf = true;
				}
			}

			image.setPixelVal( i, j, (*huff).pixelval );
		}
	}
}
